package com.eliasbalasis.dwr4gwt.client;

import java.util.ArrayList;
import java.util.Collection;

import org.juglar.gwt.jsonizer.client.Jsonizer;
import org.juglar.gwt.jsonizer.client.JsonizerException;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;

public class DwrRemoteMethodCallDescriptor {
	public static final int DEFAULT_TIMEOUT = 5000;
	private String remoteClassName;
	private String remoteMethodName;
	private Object parameterValues;
	private Jsonizer parametersMarshaller;
	private Object metadata;
	private int timeoutMsec = DEFAULT_TIMEOUT;
	private JavaScriptObject javaScriptReturnValue;
	private Integer id;
	private boolean isReturnValueOfPrimitiveType;
	private boolean isReturnValueOfArrayType;
	private String[] parameterNames = new String[0];

	private DwrRemoteMethodCallDescriptor(
			String remoteClassName,
			String remoteMethodName,
			Object parameterValues,
			Jsonizer parametersMarshaller) {
		setRemoteClassName(remoteClassName);
		setRemoteMethodName(remoteMethodName);
		setParameterValues(parameterValues);
		setParametersMarshaller(parametersMarshaller);
	}

	private DwrRemoteMethodCallDescriptor(
			String remoteClassName,
			String remoteMethodName,
			Object parameterValues,
			Jsonizer parametersMarshaller,
			String[] parameterNames) {
		this(remoteClassName,remoteMethodName,parameterValues,parametersMarshaller);
		setParameterNames(parameterNames);
	}

	public DwrRemoteMethodCallDescriptor(
			String remoteClassName,
			String remoteMethodName,
			Object parameterValues,
			Jsonizer parametersMarshaller,
			String[] parameterNames,
			Object metadata) {
		this(remoteClassName,remoteMethodName,parameterValues,parametersMarshaller,parameterNames);
		setMetadata(metadata);
	}

	public String getRemoteClassName() {
		return remoteClassName;
	}
	private void setRemoteClassName(String remoteClassName) {
		this.remoteClassName = remoteClassName;
	}

	public String getRemoteMethodName() {
		return remoteMethodName;
	}
	private void setRemoteMethodName(String remoteMethodName) {
		this.remoteMethodName = remoteMethodName;
	}

	public Object getParameterValues() {
		return parameterValues;
	}
	private void setParameterValues(Object parameterValues) {
		this.parameterValues = parameterValues;
	}

	public Jsonizer getParametersMarshaller() {
		return parametersMarshaller;
	}
	private void setParametersMarshaller(Jsonizer parametersMarshaller) {
		this.parametersMarshaller = parametersMarshaller;
	}

	public Object getMetadata() {
		return metadata;
	}
	public void setMetadata(Object metadata) {
		this.metadata = metadata;
	}

	public int getTimeoutMsec() {
		return timeoutMsec;
	}
	public void setTimeoutMsec(int timeoutMsec) {
		this.timeoutMsec = timeoutMsec;
	}

	private JavaScriptObject getJavaScriptReturnValue() {
		return javaScriptReturnValue;
	}
	void setJavaScriptReturnValue(JavaScriptObject javaScriptReturnValue) {
		this.javaScriptReturnValue = javaScriptReturnValue;
	}

	private native JavaScriptObject extractElementFromJavaScriptArray(JavaScriptObject a, int i) /*-{
		return a[i];
	}-*/;
	private native int extractLengthFromJavaScriptArray(JavaScriptObject a) /*-{
		return a.length;
	}-*/;
	public Object getReturnValue(Jsonizer jsonizer) throws DwrAdapterException {
		if (getJavaScriptReturnValue()==null)
			return null;
		Object returnValue = null;
		JavaScriptObject jsReturnValue = getJavaScriptReturnValue();
		JSONObject jsonObject = new JSONObject(jsReturnValue);
		if (isReturnValueOfPrimitiveType()) {
			JSONValue jsonValue = (JSONValue)jsonObject.get("value");
			if (jsonValue.isBoolean()!=null) {
				returnValue = new Boolean(jsonValue.isBoolean().booleanValue());
			}
			if (jsonValue.isNumber()!=null) {
				returnValue = new Double(jsonValue.isNumber().getValue());
			}
			if (jsonValue.isString()!=null) {
				returnValue = new String(jsonValue.isString().stringValue());
			}
		} else {
			if (isReturnValueOfArrayType()) {
				int index = 0;
				int length = extractLengthFromJavaScriptArray(jsReturnValue);
				ArrayList returnArray = new ArrayList();
				for (index=0;index<length;++index) {
					JavaScriptObject jsElement = extractElementFromJavaScriptArray(jsReturnValue,index);
					Object element = jsonizer.asJavaObject(jsElement);
					returnArray.add(element);
				}
				returnValue = returnArray;
			} else {
				if (jsonizer==null) {
					throw new DwrAdapterException("return value Jsonizer was expected");
				}
				try {
					returnValue = jsonizer.asJavaObject(jsReturnValue);
				} catch(JsonizerException ex) {
					throw new DwrAdapterException("Failed not convert JavaScript return value to Java Object",ex);
				}
			}
		}
		return returnValue;
	}

	public Integer getId() {
		return id;
	}
	void setId(Integer id) {
		this.id = id;
	}

	private boolean isReturnValueOfPrimitiveType() {
		return isReturnValueOfPrimitiveType;
	}
	void setReturnValueOfPrimitiveType(boolean isReturnValueOfPrimitiveType) {
		this.isReturnValueOfPrimitiveType = isReturnValueOfPrimitiveType;
	}

	private boolean isReturnValueOfArrayType() {
		return isReturnValueOfArrayType;
	}
	void setReturnValueOfArrayType(boolean isReturnValueOfArrayType) {
		this.isReturnValueOfArrayType = isReturnValueOfArrayType;
	}

	public String[] getParameterNames() {
		return parameterNames;
	}
	public void setParameterNames(String[] parameterNames) {
		this.parameterNames = parameterNames;
	}

}
